package com.wo.cloud.gateway.filter;

import cn.hutool.core.util.StrUtil;
import com.wo.cloud.gateway.props.CloudApiProperties;
import com.wo.cloud.gateway.util.Oauth2Constant;
import com.wo.cloud.gateway.util.ResponseUtil;
import com.wo.cloud.gateway.util.WoCloudConstant;
import org.apache.logging.log4j.util.Strings;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

/**
 * @author liry
 * @version 1.0
 * @date Created on 2021/3/30 22:08
 * Description: 统一网关 token 验证
 */

@Component
@EnableConfigurationProperties({CloudApiProperties.class})
public class TokenCheckGatewayFilterFactory implements GlobalFilter {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Resource
    private CloudApiProperties cloudApiProperties;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        // 获取request
        ServerHttpRequest request = exchange.getRequest();

        // 判断是否为登录，如果是则直接放行
        String path = request.getURI().getPath();
        String rawPath = request.getURI().getRawPath();

        if (isIgnoreUrl(path) || isIgnoreUrl(rawPath)) {
            // 放行
            return chain.filter(exchange);
        }

        // 获取 response
        ServerHttpResponse response = exchange.getResponse();


        // 非登录请求，则需要进行校验
        String token = request.getHeaders().getFirst(Oauth2Constant.HEADER_TOKEN);
        // 判断token是否为空
        if (StrUtil.isBlank(token)) {
            return unauthorized(response, "没有携带Token信息！");
        }

        // 判断是否为伪造，或者过期 (redis)
        token = token.replace(Oauth2Constant.TOKEN_TYPE, Strings.EMPTY);
        Boolean hasKey = redisTemplate.hasKey(Oauth2Constant.REDIS_TOKEN_PREFIX + token);
        if (!hasKey) {
            // token 不存在或伪造
            return unauthorized(response, "token不存在或者已过期");
        }

        // token存在且正确，则放行
        return chain.filter(exchange);
    }


    private Mono<Void> unauthorized(ServerHttpResponse resp, String msg) {
        return ResponseUtil.webFluxResponseWriter(resp, WoCloudConstant.JSON_UTF8, HttpStatus.UNAUTHORIZED, msg);
    }

    // 判断请求是否跳过校验
    private boolean isIgnoreUrl(String path) {
        return cloudApiProperties
                .getIgnoreUrl()
                .stream()
                .map(url -> url.replace("/**", ""))
                .anyMatch(path::startsWith);
    }

}
